"""
情感识别服务
功能：处理音频文件的情感识别逻辑
使用场景：API层调用的核心业务服务
"""

import os
import logging
import time
from typing import Dict, Any, List
from pathlib import Path

# 延迟导入避免启动时的依赖问题
import librosa
import numpy as np
from features.audio_features import AudioFeatureExtractor
from models.emotion_classifier import EmotionClassifier
from core.config import settings

logger = logging.getLogger(__name__)

class EmotionService:
    """情感识别服务类"""
    
    def __init__(self):
        """初始化情感识别服务"""
        self.feature_extractor = AudioFeatureExtractor()
        self.classifier = EmotionClassifier()
        self.model_loaded = False
        self._load_model()

    def _load_model(self):
        """
        加载模型
        内部方法：尝试加载可用的模型
        """
        try:
            # 检查是否有当前模型文件
            current_model_path = Path("models/current_model.pkl")
            if current_model_path.exists():
                self.classifier.load_model(str(current_model_path))
                self.model_loaded = True
                logger.info("当前模型加载成功")
            else:
                # 尝试加载models目录下的任意模型
                models_dir = Path("models")
                if models_dir.exists():
                    model_files = list(models_dir.glob("*.pkl"))
                    if model_files:
                        model_path = model_files[0]  # 使用第一个找到的模型
                        self.classifier.load_model(str(model_path))
                        self.model_loaded = True
                        logger.info(f"自动加载模型: {model_path}")
                    else:
                        logger.warning("未找到可用的模型文件")
                        self.model_loaded = False
                else:
                    logger.warning("models目录不存在")
                    self.model_loaded = False
        except Exception as e:
            logger.error(f"模型加载失败: {e}")
            self.model_loaded = False

    async def reload_model(self):
        """
        重新加载模型
        用于模型切换后的重新加载
        """
        self._load_model()

    async def predict_emotion(self, audio_file_path: str) -> Dict[str, Any]:
        """
        预测音频文件的情感
        参数：audio_file_path - 音频文件路径
        返回：包含预测结果的字典
        """
        start_time = time.time()
        
        try:
            # 检查模型是否已加载
            if not self.model_loaded:
                raise ValueError("模型未加载，请先下载或训练模型")
                
            # 检查文件是否存在
            if not os.path.exists(audio_file_path):
                raise FileNotFoundError(f"音频文件不存在: {audio_file_path}")
            
            logger.info(f"开始处理音频文件: {audio_file_path}")
            
            # 提取音频特征
            features = self.feature_extractor.extract_all_features(audio_file_path)
            
            if features is None or len(features) == 0:
                raise ValueError("特征提取失败，无法处理该音频文件")
            
            # 执行情感分类
            prediction_result = self.classifier.predict(features)
            
            # 计算处理时间
            processing_time = time.time() - start_time
            
            # 格式化返回结果
            result = {
                "predicted_emotion": prediction_result["predicted_class"],
                "confidence": float(prediction_result["confidence"]),
                "probabilities": {
                    emotion: float(prob) 
                    for emotion, prob in prediction_result["probabilities"].items()
                },
                "processing_time": round(processing_time, 3),
                "feature_count": int(len(features)) if isinstance(features, (list, np.ndarray)) else 0
            }
            
            logger.info(f"情感识别完成: {result['predicted_emotion']} (置信度: {result['confidence']:.3f})")
            
            return result
            
        except Exception as e:
            logger.error(f"情感预测失败: {e}")
            raise

    def is_model_available(self) -> bool:
        """
        检查模型是否可用
        返回：模型可用状态
        """
        return self.model_loaded

    def get_model_info(self) -> Dict[str, Any]:
        """
        获取当前模型信息
        返回：模型信息字典
        """
        if not self.model_loaded:
            return {
                "status": "no_model",
                "message": "没有可用的模型"
            }
        
        try:
            # 尝试获取模型详细信息
            model_info = self.classifier.get_model_info()
            return {
                "status": "ready",
                "message": "模型已就绪",
                "model_info": model_info
            }
        except Exception as e:
            return {
                "status": "error", 
                "message": f"获取模型信息失败: {e}"
            }

    def analyze_audio_features(self, audio_file_path: str) -> Dict[str, Any]:
        """
        分析音频特征详情
        参数：audio_file_path - 音频文件路径
        返回：详细的特征分析结果
        """
        try:
            # 基础音频信息
            y, sr = librosa.load(audio_file_path)
            duration = librosa.get_duration(y=y, sr=sr)
            
            # 提取各类特征
            mfcc_features = self.feature_extractor.extract_mfcc(audio_file_path)
            chroma_features = self.feature_extractor.extract_chroma(audio_file_path)
            spectral_features = self.feature_extractor.extract_spectral_features(audio_file_path)
            
            return {
                "audio_info": {
                    "duration": round(duration, 2),
                    "sample_rate": int(sr),  # 确保是 Python int
                    "samples": int(len(y))  # 确保是 Python int
                },
                "features": {
                    "mfcc": {
                        "shape": list(mfcc_features.shape) if mfcc_features is not None else None,  # 转换为 list
                        "mean": float(np.mean(mfcc_features)) if mfcc_features is not None else None
                    },
                    "chroma": {
                        "shape": list(chroma_features.shape) if chroma_features is not None else None,  # 转换为 list
                        "mean": float(np.mean(chroma_features)) if chroma_features is not None else None
                    },
                    "spectral": spectral_features
                }
            }
        except Exception as e:
            return {"error": f"特征分析失败: {str(e)}"}

    def get_service_status(self) -> Dict[str, Any]:
        """
        获取服务状态信息
        返回：服务状态字典
        """
        return {
            "is_loaded": self.model_loaded,
            "model_type": self.classifier.model_type if hasattr(self.classifier, 'model_type') else "unknown",
            "supported_emotions": settings.EMOTION_LABELS,
            "feature_extractor_status": "ready"
        }

    def batch_predict(self, audio_file_paths: List[str]) -> Dict[str, Any]:
        """
        批量预测多个音频文件的情感
        参数：audio_file_paths - 音频文件路径列表
        返回：批量预测结果
        """
        results = []
        total_processing_time = 0
        
        for file_path in audio_file_paths:
            try:
                import asyncio
                result = asyncio.run(self.predict_emotion(file_path))
                result["file_path"] = file_path
                result["status"] = "success"
                results.append(result)
                total_processing_time += result["processing_time"]
            except Exception as e:
                results.append({
                    "file_path": file_path,
                    "status": "error",
                    "error": str(e)
                })
        
        return {
            "total_files": int(len(audio_file_paths)),  # 确保是 Python int
            "successful_predictions": int(len([r for r in results if r["status"] == "success"])),  # 确保是 Python int
            "average_processing_time": round(total_processing_time / len(audio_file_paths), 3) if audio_file_paths else 0,
            "results": results
        } 